using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DragonAwakening.Core.Graphics;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace DragonAwakening.Core.Game
{
    /// <summary>
    /// Character info
    /// </summary>
    public enum CharacterInfoMode
    {
        Bags,
        Spells,
        Attributes,
        Skills
    }

    /// <summary>
    /// Character window manager.
    /// </summary>
    public class CharacterInfoView : IScreenView<Character>, IClickTarget
    {
        /// <summary>
        /// Info text top offset
        /// </summary>
        private const int CharacterInfoTopOffset = 1;

        /// <summary>
        /// Character modes map
        /// </summary>
        private Dictionary<CharacterClass, IList<CharacterInfoMode>> modes = new Dictionary<CharacterClass, IList<CharacterInfoMode>>
        {
            { CharacterClass.Barbarian, new List<CharacterInfoMode> { CharacterInfoMode.Bags, CharacterInfoMode.Attributes, CharacterInfoMode.Skills }},
            { CharacterClass.Bard,      new List<CharacterInfoMode> { CharacterInfoMode.Bags, CharacterInfoMode.Attributes, CharacterInfoMode.Skills }},
            { CharacterClass.Cleric,    new List<CharacterInfoMode> { CharacterInfoMode.Bags, CharacterInfoMode.Spells, CharacterInfoMode.Attributes, CharacterInfoMode.Skills  }},
            { CharacterClass.Druid,     new List<CharacterInfoMode> { CharacterInfoMode.Bags, CharacterInfoMode.Spells, CharacterInfoMode.Attributes, CharacterInfoMode.Skills  }},
            { CharacterClass.Fighter,   new List<CharacterInfoMode> { CharacterInfoMode.Bags, CharacterInfoMode.Attributes, CharacterInfoMode.Skills }},
            { CharacterClass.Monk,      new List<CharacterInfoMode> { CharacterInfoMode.Bags, CharacterInfoMode.Spells, CharacterInfoMode.Attributes, CharacterInfoMode.Skills  }},
            { CharacterClass.Paladin,   new List<CharacterInfoMode> { CharacterInfoMode.Bags, CharacterInfoMode.Spells, CharacterInfoMode.Attributes, CharacterInfoMode.Skills  }},
            { CharacterClass.Ranger,    new List<CharacterInfoMode> { CharacterInfoMode.Bags, CharacterInfoMode.Attributes, CharacterInfoMode.Skills }},
            { CharacterClass.Rogue,     new List<CharacterInfoMode> { CharacterInfoMode.Bags, CharacterInfoMode.Attributes, CharacterInfoMode.Skills }},
            { CharacterClass.Sorcerer,  new List<CharacterInfoMode> { CharacterInfoMode.Bags, CharacterInfoMode.Spells, CharacterInfoMode.Attributes, CharacterInfoMode.Skills  }},
            { CharacterClass.Wizard,    new List<CharacterInfoMode> { CharacterInfoMode.Bags, CharacterInfoMode.Spells, CharacterInfoMode.Attributes, CharacterInfoMode.Skills  }}
        };

        /// <summary>
        /// Character class names map.
        /// </summary>
        private Dictionary<CharacterClass, string> classNames = new Dictionary<CharacterClass, string> 
        {
            { CharacterClass.Barbarian, "Barbarian" },
            { CharacterClass.Bard, "Bard" },
            { CharacterClass.Cleric, "Cleric" },
            { CharacterClass.Druid, "Druid" },
            { CharacterClass.Fighter, "Fighter" },
            { CharacterClass.Monk, "Monk" },
            { CharacterClass.Paladin, "Paladin" },
            { CharacterClass.Ranger, "Ranger" },
            { CharacterClass.Rogue, "Rogue" },
            { CharacterClass.Sorcerer, "Sorcerer" },
            { CharacterClass.Wizard, "Wizard" },
        };

        /// <summary>
        /// Mode buttons
        /// </summary>
        private Dictionary<CharacterInfoMode, Texture2D> modeButtonTextures = new Dictionary<CharacterInfoMode, Texture2D>();

        /// <summary>
        /// Close button rectangle.
        /// </summary>
        private Rectangle closeButtonRect = new Rectangle(743, 1, 56, 56);

        /// <summary>
        /// Current info screen mode.
        /// </summary>
        private CharacterInfoMode current = CharacterInfoMode.Bags;

        /// <summary>
        /// Character info texture.
        /// </summary>
        private Texture2D characterInfoTexture;

        /// <summary>
        /// Character info texture.
        /// </summary>
        private Texture2D closeButtonTexture;

        /// <summary>
        /// Character info texture.
        /// </summary>
        private Texture2D dataBackgroundTexture;

        /// <summary>
        /// Font
        /// </summary>
        private SpriteFont font;

        /// <summary>
        /// Game context
        /// </summary>
        private IGameContext context;

        /// <summary>
        /// Nested views.
        /// </summary>
        private Dictionary<CharacterInfoMode, IScreenView<Character>> views = new Dictionary<CharacterInfoMode, IScreenView<Character>>();

        /// <summary>
        /// Constructs the character info view.
        /// </summary>
        public CharacterInfoView(SpriteFont font, ITextureManager textureManager, IGameContext context)
        {
            font.RequiredNotNull("font");
            context.RequiredNotNull("context");

            this.font = font;
            this.context = context;

            this.characterInfoTexture = textureManager.Get("character_info_stats");
            this.closeButtonTexture = textureManager.Get("character_frame_btn_close");
            this.modeButtonTextures.Add(CharacterInfoMode.Bags, textureManager.Get("character_frame_btn_bags"));
            this.modeButtonTextures.Add(CharacterInfoMode.Spells, textureManager.Get("character_frame_btn_spells"));
            this.modeButtonTextures.Add(CharacterInfoMode.Skills, textureManager.Get("character_frame_btn_skills"));
            this.modeButtonTextures.Add(CharacterInfoMode.Attributes, textureManager.Get("character_frame_btn_attributes"));

            this.dataBackgroundTexture = textureManager.GetColorTexture(Color.FromNonPremultiplied(78, 54, 2, 255));

            this.views.Add(CharacterInfoMode.Attributes, new CharacterInfoAttributesView(font, textureManager, context));
            this.views.Add(CharacterInfoMode.Bags, new CharacterInfoBagView(font, textureManager, context));
        }

        /// <summary>
        /// Draws the character window.
        /// </summary>
        public void Draw(IViewPort view, Character data)
        {
            var buttonOffset = new Vector2(800 - 57, 1);
            view.Draw(this.closeButtonTexture, buttonOffset, Color.White);

            buttonOffset.Y += this.closeButtonTexture.Height - 1;

            foreach (var mode in modes[data.Class])
            {
                var buttonTexture = this.modeButtonTextures[mode];
                view.Draw(buttonTexture, buttonOffset, Color.White);
                buttonOffset.Y += buttonTexture.Height - 1;
            }

            var attributesWindowRect = new Rectangle(425, 1, 318, 474);
            view.Draw(this.dataBackgroundTexture, attributesWindowRect, Color.White);
            view.DrawRect(attributesWindowRect, Color.Black, 1);

            view.Draw(this.characterInfoTexture, new Vector2(130, 1), Color.White);

            var raceString = data.Race.ToString();
            view.DrawString(font, raceString, new Vector2(136, CharacterInfoTopOffset), Color.White);
            
            var classString = this.classNames[data.Class];
            view.DrawString(font, classString, new Vector2(136 + 117, CharacterInfoTopOffset), Color.White);

            var levelString = data.Level.ToString();
            view.DrawString(font, levelString, new Vector2(136 + 255, CharacterInfoTopOffset), Color.White);

            if (this.views.ContainsKey(this.current))
            {
                this.views[this.current].Draw(view, data);
            }
        }

        /// <summary>
        /// Processing click event.
        /// </summary>
        /// <param name="click"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public bool OnClick(Point click, IGameContext context)
        {
            if (!context.CurrentState.Is<CharacterStatsGameState>())
            {
                return false;
            }

            if (closeButtonRect.Contains(click))
            {
                this.current = CharacterInfoMode.Bags;
                context.SetState(GameState.Instance);
                return true;
            }

            var character = ((CharacterStatsGameState)context.CurrentState).Character;
            var modesData = modes[character.Class];
            var tabRect = new Rectangle(744, 58, 56, modesData.Count * 102);
            if (tabRect.Contains(click))
            {
                int tabIndex = (click.Y - 57) / 102;
                this.current = modesData[tabIndex];
            }

            return false;
        }

        /// <summary>
        /// Character info attributes nested view.
        /// </summary>
        public class CharacterInfoAttributesView : IScreenView<Character>, IClickTarget
        {
            /// <summary>
            /// Font
            /// </summary>
            private SpriteFont font;

            /// <summary>
            /// Game context
            /// </summary>
            private IGameContext context;

            /// <summary>
            /// Primary attributes data source.
            /// </summary>
            private KeyValuePair<string, Func<Character, MaxValueData>>[][] dataSource = new KeyValuePair<string, Func<Character, MaxValueData>>[][]
            {
                new KeyValuePair<string, Func<Character, MaxValueData>>[]
                {
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Strength", x => x.Strength),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Intellect", x => x.Intellect),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Dexterity", x => x.Dexterity),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Vitality", x => x.Vitality),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Spirit", x => x.Spirit),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Speed", x => x.Speed)
                },
                new KeyValuePair<string, Func<Character, MaxValueData>>[]
                {
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Armor", x => x.ArmorClass),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Power", x => x.Power),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Miss", x => x.Miss),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Critical", x => x.Critical),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Dodge", x => x.Dodge),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Block", x => x.Block)
                },
                new KeyValuePair<string, Func<Character, MaxValueData>>[]
                {
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Health", x => x.Health),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Mana", x => x.Mana),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Stamina", x => x.Stamina),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Experience", x => x.Experience),
                    new KeyValuePair<string, Func<Character, MaxValueData>>("Carry", x => x.Carry)
                }
            };

            /// <summary>
            /// Separator line texture
            /// </summary>
            private Texture2D separatorLineTexture;

            /// <summary>
            /// Current max view with delta.
            /// </summary>
            private CurrentMaxDeltaAttributeValueView currentMaxDeltaView = new CurrentMaxDeltaAttributeValueView();

            /// <summary>
            /// Current max view.
            /// </summary>
            private CurrentMaxAttributeValueView currentMaxView = new CurrentMaxAttributeValueView();

            /// <summary>
            /// Percentage view.
            /// </summary>
            private PercentCurrentMaxAttributeValueView percentageView = new PercentCurrentMaxAttributeValueView();

            /// <summary>
            /// Constructs the attributes view.
            /// </summary>
            public CharacterInfoAttributesView(SpriteFont font, ITextureManager textureManager, IGameContext context)
            {
                this.font = font;
                this.context = context;
                this.separatorLineTexture = textureManager.GetColorTexture(Color.Gray);
            }

            /// <summary>
            /// Draws the attributes information.
            /// </summary>
            public void Draw(IViewPort view, Character data)
            {
                var firstGroup = this.dataSource[0];
                this.DrawGroup(view, this.dataSource[0], new Vector2(445, 7), 120, 50, 25, data, this.currentMaxDeltaView);
                this.DrawGroup(view, this.dataSource[1], new Vector2(445, 24+150), 120, 50, 25, data, this.percentageView);
                this.DrawGroup(view, this.dataSource[2], new Vector2(445, 31+300), 120, 50, 25, data, this.currentMaxView);
            }

            /// <summary>
            /// Draws the group of attributes
            /// </summary>
            private void DrawGroup(IViewPort view, IEnumerable<KeyValuePair<string, Func<Character, MaxValueData>>> group, Vector2 startOffset, int columnKeyWidth, int columnValueWidth, int step, Character character, IAttributeValueView attributeView)
            {
                Vector2 keyColumn = startOffset;
                Vector2 valueColumn = startOffset + new Vector2(columnKeyWidth, 0);
                Vector2 stepOffset = new Vector2(0, step);

                foreach (var item in group)
                {
                    view.DrawString(this.font, item.Key, keyColumn, Color.White);

                    attributeView.Draw(view, item.Value(character), valueColumn, this.font);

                    keyColumn += stepOffset;
                    valueColumn += stepOffset;
                }
            }

            /// <summary>
            /// Processes the click event.
            /// </summary>
            public bool OnClick(Point click, IGameContext context)
            {
                return false;
            }

            /// <summary>
            /// Attribute value view.
            /// </summary>
            public interface IAttributeValueView
            {
                /// <summary>
                /// Draw attrbiute
                /// </summary>
                void Draw(IViewPort view, MaxValueData data, Vector2 position, SpriteFont font);
            }

            /// <summary>
            /// Current (Max - delta) attribute view
            /// </summary>
            public class CurrentMaxDeltaAttributeValueView : IAttributeValueView
            {
                /// <summary>
                /// Draw attrbiute
                /// </summary>
                public void Draw(IViewPort view, MaxValueData data, Vector2 position, SpriteFont font)
                {
                    int delta = data.Current - data.Max;
                    if (delta == 0)
                    {
                        view.DrawString(font, data.Current.ToString(), position, Color.White);
                    }
                    else
                    {
                        string value = data.Current.ToString();
                        var offset = font.MeasureString(value);

                        view.DrawString(font, value, position, Color.White);

                        position.X += offset.X + 5;

                        string openString = string.Concat("(", data.Max.ToString());
                        offset = font.MeasureString(openString);
                        view.DrawString(font, openString, position, Color.White);

                        position.X += offset.X;

                        string deltaString = string.Concat(delta > 0 ? "+" : "-", Math.Abs(delta));
                        offset = font.MeasureString(deltaString);
                        view.DrawString(font, deltaString, position, delta > 0 ? Color.LightGreen : Color.Red);

                        position.X += offset.X;

                        string closeString = ")";
                        view.DrawString(font, closeString, position, Color.White);
                    }
                }
            }

            /// <summary>
            /// Current / Max attribute view
            /// </summary>
            public class CurrentMaxAttributeValueView : IAttributeValueView
            {
                /// <summary>
                /// Draw attrbiute
                /// </summary>
                public void Draw(IViewPort view, MaxValueData data, Vector2 position, SpriteFont font)
                {
                    string value = string.Format("{0} / {1}", data.Current, data.Max);
                    view.DrawString(font, value, position, Color.White);
                }
            }

            /// <summary>
            /// Current / Max attribute view
            /// </summary>
            public class PercentCurrentMaxAttributeValueView : IAttributeValueView
            {
                /// <summary>
                /// Draw attrbiute
                /// </summary>
                public void Draw(IViewPort view, MaxValueData data, Vector2 position, SpriteFont font)
                {
                    string value = string.Format("{0} / {1}%", data.Current, ((int)(data.Percentage * 100.0)).ToString());
                    view.DrawString(font, value, position, Color.White);
                }
            }
        }

        /// <summary>
        /// Bag view
        /// </summary>
        public class CharacterInfoBagView : IScreenView<Character>, IClickTarget
        {
            /// <summary>
            /// Font
            /// </summary>
            private SpriteFont font;

            /// <summary>
            /// Game context
            /// </summary>
            private IGameContext context;

            /// <summary>
            /// Bag slot texture
            /// </summary>
            private Texture2D texture;

            /// <summary>
            /// Constructs the attributes view.
            /// </summary>
            public CharacterInfoBagView(SpriteFont font, ITextureManager textureManager, IGameContext context)
            {
                this.font = font;
                this.context = context;
                this.texture = textureManager.Get("bag_slot");
            }
            
            /// <summary>
            /// Draw bag information.
            /// </summary>
            public void Draw(IViewPort view, Character data)
            {
                int bagItemIndex = 0;
                for (int y = 0; y < 7; y++)
                for (int x = 0; x < 5; x++)
                {
                    Vector2 position = new Vector2(426 + x * 61 + 4, 2 + y * 61 + 10);
                    view.Draw(this.texture, position, Color.White, 0.8f);
                }

                var bagInfo = string.Format("Carry: {0} / {1} ({2}%)", data.Carry.Current, data.Carry.Max, data.Carry.Percentage * 100);
                view.DrawString(this.font, bagInfo, new Vector2(434, 443), Color.White);
            }

            /// <summary>
            /// On click
            /// </summary>
            public bool OnClick(Point click, IGameContext context)
            {
                return false;
            }
        }
    }
}
